package com.cloud.rou.im.cluster.naming;

import com.cloud.rou.im.cluster.naming.node.ImServerInstance;
import com.cloud.rou.im.cluster.naming.node.InstanceTreeCacheListener;
import com.cloud.rou.im.cluster.zk.ZookeeperClientFactory;
import com.cloud.rou.im.core.util.SingletonUtil;
import com.cloud.rou.im.protocol.serialize.FastJsonSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.listen.Listenable;
import org.apache.curator.framework.recipes.cache.ChildData;
import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
import org.apache.curator.framework.recipes.cache.TreeCacheListener;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;

/**
 * <p></p>
 *
 * @author GaoShuangWen
 * @since 2019-11-08 20:52
 */
@Slf4j
public class NameService {
    //Zk Curator 客户端
    private CuratorFramework client = ZookeeperClientFactory.getInstance().getZkCli();
    //保存当前ZNode节点的路径, 创建后返回
    private String pathRegistered = null;
    private ImServerInstance instance;
    private FastJsonSerializer serializer = FastJsonSerializer.getInstance();

    //取得唯一的实例
    public static NameService getInst() {
        return SingletonUtil.get(NameService.class);
    }


    private NameService() {

    }


    /**
     *  创建临时节点
     * @param instance
     */
    public void registeredNode(ImServerInstance instance) {
        this.instance = instance;
        createParentIfNeeded("/im/instance");
        // 创建一个ZNode节点
        // 节点的 payload 为当前Worker 实例
        try {
            byte[] payload = serializer.serialize(instance);
            pathRegistered = client.create()
                    .creatingParentsIfNeeded()
                    // 设置为临时节点
                    .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
                    .forPath("/im/instance/id-", payload);
            //为node 设置id
            instance.setId(getId());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 取得IM 节点编号
     *
     * @return 编号
     */
    public long getId() {
        String sid = null;
        if (null == pathRegistered) {
            throw new RuntimeException("节点注册失败");
        }
        int index = pathRegistered.lastIndexOf("/im/instance/id-");
        if (index >= 0) {
            index += "/im/instance/id-".length();
            sid = index <= pathRegistered.length() ?
                    pathRegistered.substring(index) : null;
        }
        if (null == sid) {
            throw new RuntimeException("节点ID生成失败");
        }
        return Long.parseLong(sid);
    }

    /**
     * 增加负载, 表示有用户登录成功
     *
     * @return 成功状态
     */
    public synchronized boolean incBalance() {
        if (null == instance) {
            throw new RuntimeException("还没有设置Node 节点");
        }
        // 增加负载：增加负载, 并写回ZooKeeper
        while (true) {
            try {
                instance.getBalance().getAndIncrement();
                byte[] payload = serializer.serialize(this.instance);
                client.setData().forPath(pathRegistered, payload);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
    }

    /**
     * 减少负载, 表示有用户下线
     *
     * @return 成功状态
     */
    public synchronized boolean decrBalance() {
        if (null == instance) {
            throw new RuntimeException("还没有设置Node 节点");
        }
        // 增加负载, 并写回ZooKeeper
        while (true) {
            try {
                int i = instance.getBalance().decrementAndGet();
                if (i < 0) {
                    instance.getBalance().set(0);
                }
                byte[] payload = serializer.serialize(this);
                client.setData().forPath(pathRegistered, payload);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
    }

    /**
     * 创建父节点
     *
     * @param
     */
    private void createParentIfNeeded(String managePath) {
        try {
            Stat stat = client.checkExists().forPath(managePath);
            if (null == stat) {
                client.create()
                        .creatingParentsIfNeeded()
                        .withProtection()
                        .withMode(CreateMode.PERSISTENT)
                        .forPath(managePath);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

